Skip to content

move and delete books manually#204

Open
elfkuzco wants to merge 3 commits intomainfrom
delete-books
Open

move and delete books manually#204
elfkuzco wants to merge 3 commits intomainfrom
delete-books

Conversation

@elfkuzco
Copy link
Contributor

@elfkuzco elfkuzco commented Mar 10, 2026

Rationale

This PR adds functionality to move, recover and delete books from the API/UI.
Screenshot_20260310_045108
Screenshot_20260310_045044
Screenshot_20260310_045027

Changes

  • add endpoints to delete, move and recover book
  • move quarantine and staging related warehouse env vars from mill to common context as they are now used by the API layer too
  • enhance get_book_or_none function to allow for passing params for filtering instead of having if...else checks for attributes
  • add db functions to move, recover and delete book
  • previously, the shuttle delete_files logic set the book has_error if deletion failed. This doesn't seem correct as the book does not necessarily have an error. Instead, in addition to logging the exception message, set the needs_file_operation to False
  • remove the has_error flag from filters to get_next_book_to_delete as we want to now delete books that have errors too
  • add UI buttons to move, recover and delete book
  • only include quarantine, staging and/or prod books in library.xml generation (filter added in DB query get_latest_books_for_collection)

This closes #143

@elfkuzco elfkuzco self-assigned this Mar 10, 2026
@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 89.16667% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.19%. Comparing base (5d55224) to head (83af63f).

Files with missing lines Patch % Lines
backend/src/cms_backend/db/book.py 90.69% 5 Missing and 3 partials ⚠️
backend/src/cms_backend/api/routes/books.py 84.21% 3 Missing ⚠️
backend/src/cms_backend/mill/context.py 0.00% 1 Missing ⚠️
backend/src/cms_backend/shuttle/move_files.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #204      +/-   ##
==========================================
- Coverage   80.39%   80.19%   -0.21%     
==========================================
  Files          47       47              
  Lines        1974     2065      +91     
  Branches      192      205      +13     
==========================================
+ Hits         1587     1656      +69     
- Misses        334      353      +19     
- Partials       53       56       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@elfkuzco elfkuzco requested a review from benoit74 March 10, 2026 04:14
Copy link
Contributor

@benoit74 benoit74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good, thank you. I've identified few small flaws and I will open few more issues for stuff not yet identified as necessary for a smooth user experience.

Fix book status when book is pending deletion (both in book details and title details views), we should not have "Unknown" shown here:

Image

Also when I mark a book for immediate "force deletion", the "recover" button is still shown (this might be acceptable) but the API call fails (this is expected) and the message says "Book ... is not eligible for deletion" (this is incorrect)

Image

Display location_kind next to the green checkmark (in addition to the tooltip, both in book details and title details views):

Image

When book is deleted, status is back to "Published" (in the UI) while it should be "Deleted" (both in book details and title details view)

Image

f"Book {book_id} does not meet criteria to be deleted."
)

deletion_date = now if force_delete else now + Context.old_book_deletion_delay
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename old_book_deletion_delay to book_deletion_delay (including env var)

deletion_date = now if force_delete else now + Context.old_book_deletion_delay

if book.location_kind == "quarantine":
if book.has_error:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's simplify this logic and always delete books at deletion_date, looking again at this, it is going to be too confusing for users to have two distinct behaviors. Sorry for the late change.

for loc in current_locations:
if loc.warehouse_id == Context.quarantine_warehouse_id:
try:
loc.path.relative_to(Context.quarantine_base_path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not using is_relative_to which would avoid the try/except block?

)
logger.exception(f"Failed to delete files for book {book.id}")
book.has_error = True
book.needs_file_operation = False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit embarrassed by this because it means we will never see that a book which is supposed to be deleted is still present on the filesystem (because tbh, nobody will go have a look after events and logs if not alarmed by some flag).

Could you explain again what is the downside of setting has_error to True? I don't mind about setting needs_file_operation to False, this is ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I forgot that I no longer use has_error in the logic to get_next_book_to_delete. So, it can be safely added here.

if not book:
break

logger.info("Moving ZIM files")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why making this log at every book we move?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mistake. Logs kept appearing even when no book was being moved. Moved it now to be Moving ZIM files for book {book_id} when something is actually being moved.

</v-alert>

<div v-if="book" class="mt-4 pa-3 bg-grey-lighten-4 rounded">
<div class="text-caption text-grey mb-1">Book ID:</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please display as well (no need for the copy button for these fields) the book name, flavour, date and created_at.

Maybe something more "condensed" like on "recover" popup would look better, especially on small screens. Not sure about that tbh.

<v-dialog v-model="isOpen" max-width="600px" persistent>
<v-card>
<v-card-title class="text-h5 pa-4 bg-primary">
<span class="text-white">Move Book</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please display again the book name, flavour, date and created_at ; can probably help to avoid mistakes.

This will recover the book and cancel the deletion process.
</v-alert>

<div v-if="book" class="mb-4">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the book ID (no-one will check that) but display as well the book date and created_at ; can probably help to avoid mistakes.

</v-alert>

<v-select
v-model="destination"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there is only one available destination (which is usually the case), then this destination should be automatically selected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow to move and delete books manually

2 participants